package com.meaningful.design_pattern.composite_pattern.expression;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class ExpressionParser {

    private final String infixExpression;

    int point = 0;

    public ExpressionParser(String infixExpression) {
        this.infixExpression = infixExpression;
    }

    public List<String> toSuffix() throws Exception {
        List<String> suffix = new ArrayList<String>();
        LinkedList<String> stack = new LinkedList<>();

        while (point < infixExpression.length()) {

            char c = infixExpression.charAt(point);

            if (c == '(') {
                stack.push(c + "");
            } else if (c == ')') {
                while (!stack.peek().equals("(")) {
                    suffix.add(stack.pop());
                }
                stack.pop();
            } else if (c == '*' || c == '/') {
                while (!stack.isEmpty() && (stack.peek().equals("*") || stack.peek().equals("/"))) {
                    suffix.add(stack.pop());
                }
                stack.push(c + "");
            } else if (c == '+' || c == '-') {
                while (isOperator(stack)) {
                    suffix.add(stack.pop());
                }
                stack.push(c + "");
            } else if (Character.isDigit(c)) {
                StringBuilder stringBuilder = new StringBuilder();
                while (point < infixExpression.length() && Character.isDigit(infixExpression.charAt(point))) {
                    stringBuilder.append(infixExpression.charAt(point));
                    point++;
                }
                point--;
                suffix.add(stringBuilder.toString());
            } else {
                throw new IllegalStateException("非法字符");
            }

            point++;
        }

        while (!stack.isEmpty()) {
            suffix.add(stack.pop());
        }

        return suffix;
    }

    public Expression parse() throws Exception {
        List<String> suffix = this.toSuffix();
        LinkedList<Expression> stack = new LinkedList<>();

        for (String item : suffix) {
            if (item.equals("+")) {
                Expression right = stack.pop();
                stack.push(new AddExpression(stack.pop(), right));
            } else if (item.equals("-")) {
                Expression right = stack.pop();
                stack.push(new SubtractionExpression(stack.pop(), right));
            } else if (item.equals("*")) {
                Expression right = stack.pop();
                stack.push(new MultiplicationExpression(stack.pop(), right));
            } else if (item.equals("/")) {
                Expression right = stack.pop();
                stack.push(new DivisionExpression(stack.pop(), right));
            } else {
                stack.push(new NumberExpression(Integer.parseInt(item)));
            }
        }

        return stack.pop();
    }

    private boolean isOperator(LinkedList<String> stack) {
        if (stack.isEmpty()) {
            return false;
        }
        return Set.of("+", "-", "*", "/").contains(stack.peek());
    }

}
